package ngat.oss;

import ngat.net.*;
import ngat.net.transaction.*;

import ngat.message.base.*;
import ngat.message.OSS.*;

import java.util.*;

/** Class for handling OSS commands sent via JMS/MA.
 * This class operates by submitting the TRANSACTION to the
 * OSS Transaction Executor directly then waiting around, polling 
 * for completion and sending ACKs to the client each poll cycle.
 */
public class OSSCommandImpl implements RequestHandler {

    /** Interval for polling TMX for completion status.*/
    public static final long DEFAULT_POLLING_INTERVAL = 1000L;

    /** How long a transaction can be queued before it is kicked off the queue.*/
    public static final long MAX_TIME_IN_QUEUE = 5*60*1000L;

    /** Fudge factor for polling/sleep timeout.*/
    public static final double FUDGE = 5.0; 

    /** How much we backoff the polling interval after a while.*/
    public static final double BACKOFF_FACTOR = 1.2;

    Vector queues;

    /** ServerImpl to send ACKs and DONE via.*/
    JMSMA_ProtocolServerImpl server;

    /** The Transaction request.*/
    TRANSACTION transaction;

    /** The queue we shall be using.*/
    List myqueue;

    /** Create an OSSCommandImpl.
     * @param server      ServerImpl to send ACKs and DONE via.
     * @param transaction The Transaction request.
     * @param queue       The Transaction queue.
     */
    public OSSCommandImpl(JMSMA_ProtocolServerImpl server, 
			  TRANSACTION              transaction, 
			  Vector                   queues) {
	this.server      = server;
	this.transaction = transaction;
	this.queues       = queues;
    }

    /** Handles the request.*/
    public void handleRequest() {

	ClientDescriptor cd = transaction.getClientDescriptor();

	System.err.println("Recieved JMS Transaction with CD="+cd+" Qs="+queues.size());

	ack((int)(FUDGE*DEFAULT_POLLING_INTERVAL*20));

	TransactionUnit tu = new TransactionUnit(transaction);
	int tp = transaction.getTransactionPriority();
	
	if (tp >= queues.size())
	    myqueue = (List)queues.lastElement();
	else
	    myqueue = (List)queues.get(tp);
	
	myqueue.add(tu);

	int count = 0;
	long poll = DEFAULT_POLLING_INTERVAL;

	// Wait for it to be executed.
	while (! tu.isCompleted()) {

	    // ### We should start off polling fast then relax - what params? ####
	    // ### e.g. INIT_POLL_INT, BACKOFF_FACTOR, COUNTS_TO_BACKOFF etc
	    
	    count++;
	    double timeInQueue = ((double)count*(double)DEFAULT_POLLING_INTERVAL);
	    if (timeInQueue > MAX_TIME_IN_QUEUE) { 
		TRANSACTION_DONE done = tu.getFailed(600101, 
						     "Transaction overrun time-in-queue after "+MAX_TIME_IN_QUEUE+" ms");
		System.err.println("OSSCmdImpl::Time in queue exceeded for: "+tu);
		// should be out anyway but should do no harm ?
		myqueue.remove(tu);
		server.sendDone(done);
		return;
	    }
	    
	    //   if (count == COUNTS_TO_BACKOFF) {
	    //poll = (long)((double)poll*BACKOFF_FACTOR);
	    //}

	    // We poll faster than the ACK rate as the polling request 
	    // can be blocked by sync lock at TMX.
	    System.err.println("OSSCmdImpl::Count "+count+" mod "+(count % 20));
	    if (count % 20 == 0) {
		int acktime = (int)(FUDGE*DEFAULT_POLLING_INTERVAL*20);
		System.err.println("OSSCmdImpl::Acking with timeout: "+acktime+" ms");
		ack(acktime);
	    }

	    // sleep
	    try { Thread.sleep(DEFAULT_POLLING_INTERVAL); } catch (InterruptedException ix) {}
	    
	    // log status.

	    //completed = (status == TransactionOptions.DONE_STATUS);	  
	}


	TRANSACTION_DONE done = tu.getDone();	
	server.sendDone(done);

    }

    /** Send an ACK to the client (via serverImpl).*/
    protected void ack(int timeout) {
	ACK ack = new ACK(transaction.getId());
	ack.setTimeToComplete(timeout);
	server.sendAck(ack);	
    }

    /** Provides an indication of the expected
     * execution time of the handleRequest() method in msecs.*/
    public long getHandlingTime() { return 20*DEFAULT_POLLING_INTERVAL; }
    
    /** Called to allow the handler to release resources before disposal.*/
    public void dispose() {
	server = null;
	transaction = null;
	//tmx = null;
    }

    /** Called if an Exception is thrown by another thread.*/
    public void exceptionOccurred(Object source, Exception e) {
	e.printStackTrace();
    }

}
